package com.docx4j.util;

import org.docx4j.TraversalUtil;
import org.docx4j.XmlUtils;
import org.docx4j.dml.CTNonVisualDrawingProps;
import org.docx4j.dml.wordprocessingDrawing.*;
import org.docx4j.finders.RangeFinder;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.*;

import javax.xml.bind.JAXBElement;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class DocxUtils {

    public final static Integer HEADER_TOP_OFFSET = 0;


    public static <T> List<T> findElement(Object obj, Class<T> cls) {
        List<T> list = new ArrayList<T>();
        Object elt = null;
        if (obj instanceof JAXBElement) {
            elt = ((JAXBElement) obj).getValue();
//            System.out.println("Element type is  : " + elt.getClass());
        } else {
            elt = obj;
        }

        if (elt.getClass().equals(cls)) {
            list.add(cls.cast(elt));
        } else if (elt instanceof ContentAccessor) {
            List children = ((ContentAccessor) elt).getContent();
            for (Object child : children) {
                list.addAll(findElement(child, cls));
            }
        }
        return list;
    }

    public static void writeImage(WordprocessingMLPackage wordMLPackage, MainDocumentPart documentPart, String searchKey, String imagePath) throws Exception {
        List<Tbl> tblList = findElement(documentPart, Tbl.class);

        System.out.println("Table's size : " + tblList.size());

        for (Tbl table : tblList) {
            List<Tr> rows = findElement(table, Tr.class);
            System.out.println("Table.row's size : " + rows.size());
            for (Tr tr : rows) {
                List<Tc> cols = findElement(tr, Tc.class);
                System.out.println("Table.row.col's size : " + cols.size());
                for (Tc tc : cols) {
                    List<Text> textList = findElement(tc, Text.class);
                    System.out.println("Table.row.col.text's size : " + textList.size());
                    for (Text text : textList) {
                        System.out.println("Table : " + text + "\t:\t" + text.getValue());
                        if (text.getValue().equalsIgnoreCase(searchKey)) {
//                            P paragraphWithImage = addInlineImageToParagraph(createInlineImage(wordMLPackage, imagePath));
                            P paragraphWithImage = newImageAnchor(wordMLPackage, imagePath, "2021年4月23日");
                            tc.getContent().remove(0);
                            tc.getContent().add(paragraphWithImage);
                        }
                    }
                }
            }
        }
    }

    public static P addInlineImageToParagraph(Inline inline) {
        // Now add the in-line image to a paragraph
        ObjectFactory factory = new ObjectFactory();
        P paragraph = factory.createP();
        R run = factory.createR();
        paragraph.getContent().add(run);
        Drawing drawing = factory.createDrawing();
        run.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        return paragraph;
    }


    public static Inline createInlineImage(WordprocessingMLPackage wordMLPackage, String imagePath) throws Exception {
        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, new File(imagePath));
        int docPrId = 100;
        int cNvPrId = 200;
        return imagePart.createImageInline("Filename hint", "Alternative text", docPrId, cNvPrId, false);
    }

    public static P newImageAnchor(WordprocessingMLPackage wordMLPackage, String imagePath, String text) throws Exception {
        String filenameHint = "", altText = "";
        int id1 = 0, id2 = 0;

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, new File(imagePath));

//        Anchor inline = imagePart.createImageAnchor(filenameHint, altText, id1, id2, false, 100, 200);
        Inline inline = imagePart.createImageInline(filenameHint, altText, id1, id2, false);
        // convert the inline to an anchor (xml contents are essentially the same)
        String anchorXml = XmlUtils.marshaltoString(inline, true, false, Context.jc, Namespaces.NS_WORD12, "anchor", Inline.class);
        ObjectFactory factory = Context.getWmlObjectFactory();
        P p = factory.createP();
        R run = factory.createR();
        if (text != null) {
            Text txt = factory.createText();
            txt.setValue(text);
            run.getContent().add(txt);
        }
        p.getContent().add(run);
        org.docx4j.wml.Drawing drawing = factory.createDrawing();
        run.getContent().add(drawing);
        drawing.getAnchorOrInline().add(inline);
        return p;
    }

    public static R newImageRun(WordprocessingMLPackage wordMLPackage, MainDocumentPart documentPart, String imagePath) throws Exception {

        String filenameHint = "", altText = "";
        int id1 = 0, id2 = 0;

        BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, documentPart, new File(imagePath));

        Inline inline = imagePart.createImageInline(filenameHint, altText, id1, id2, false);

        // convert the inline to an anchor (xml contents are essentially the same)
        String anchorXml = XmlUtils.marshaltoString(inline, true, false, Context.jc, Namespaces.NS_WORD12, "anchor",
                Inline.class);

        org.docx4j.dml.ObjectFactory dmlFactory = new org.docx4j.dml.ObjectFactory();
        org.docx4j.dml.wordprocessingDrawing.ObjectFactory wordDmlFactory = new org.docx4j.dml.wordprocessingDrawing.ObjectFactory();

        Anchor anchor = (Anchor) XmlUtils.unmarshalString(anchorXml, Context.jc, Anchor.class);
        anchor.setSimplePos(dmlFactory.createCTPoint2D());
        anchor.getSimplePos().setX(0L);
        anchor.getSimplePos().setY(0L);
        anchor.setSimplePosAttr(false);
        anchor.setPositionH(wordDmlFactory.createCTPosH());
        anchor.getPositionH().setAlign(STAlignH.RIGHT);
        anchor.getPositionH().setRelativeFrom(STRelFromH.MARGIN);
        anchor.setPositionV(wordDmlFactory.createCTPosV());
        anchor.getPositionV().setPosOffset(HEADER_TOP_OFFSET);
        anchor.getPositionV().setRelativeFrom(STRelFromV.PAGE);
        anchor.setWrapNone(wordDmlFactory.createCTWrapNone());

        // Now add the inline in w:p/w:r/w:drawing
        ObjectFactory factory = Context.getWmlObjectFactory();
        org.docx4j.wml.Drawing drawing = factory.createDrawing();
        R run = factory.createR();
        run.getContent().add(drawing);

        drawing.getAnchorOrInline().add(anchor);

        return run;
    }

    public static void listMainDocumentPart(Object obj, int level) {
        if (obj instanceof JAXBElement) {
            Object elt = ((JAXBElement) obj).getValue();
            if (elt instanceof Text) {
                System.out.println(level + " : " + elt + "\t:\t" + ((Text) elt).getValue());
            } else if (elt instanceof CTBookmark) {
                System.out.println(level + " : " + elt + "\t:\t" + ((CTBookmark) elt).getName());
            } else if (elt instanceof CTMarkupRange) {
                System.out.println(level + " : " + elt + "\t:\t" + ((CTMarkupRange) elt).getDisplacedByCustomXml());
            }
        }
        if (obj instanceof ContentAccessor) {
            List children = ((ContentAccessor) obj).getContent();
            if (children.size() > 0) level++;
            for (Object child : children) {
                listMainDocumentPart(child, level);
            }
        }
    }

    /**
     * 替换书签为图片
     * <p>
     * https://github.com/izhangzhihao/OfficeProducer/blob/master/src/main/java/com/github/izhangzhihao/OfficeProducer/DocxProducer.java
     *
     * @param wordMLPackage
     * @param documentPart
     * @param imageParameters
     * @throws Exception
     */
    public static void replaceBookMarkWithImage(WordprocessingMLPackage wordMLPackage,
                                                MainDocumentPart documentPart,
                                                HashMap<String, String> imageParameters)
            throws Exception {
        Document wmlDoc = documentPart.getContents();
        Body body = wmlDoc.getBody();
        // 提取正文中所有段落
        List<Object> paragraphs = body.getContent();
        // 提取书签并创建书签的游标
        RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
        new TraversalUtil(paragraphs, rt);

        // 遍历书签
        for (CTBookmark bm : rt.getStarts()) {
            String bookmarkName = bm.getName();
            String imagePath = imageParameters.get(bookmarkName);
            if (imagePath != null) {
                /*
                // 创建一个行内图片
                BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, new File(imagePath));
                // createImageInline函数的前四个参数我都没有找到具体啥意思
                // 最后一个是限制图片的宽度，缩放的依据
//                Inline inline = imagePart.createImageInline(null, null, 0, 1, false, 800);
                Inline inline = imagePart.createImageInline(null, null, -100, -200, false);
                */
                MyBinaryPartAbstractImage imagePart = MyBinaryPartAbstractImage.createImagePart(wordMLPackage, documentPart, new File(imagePath));
                Anchor anchor = imagePart.createImageAnchor(wordMLPackage, null, null, 0, 0, 2500, 2500, 1514400, 0);
                // 获取该书签的父级段落
                P p = (P) (bm.getParent());

                // 设置段落水平对齐方式
                /*
                PPr pprop = p.getPPr();
                if (pprop == null) {
                    pprop = new PPr();
                    Jc align = new Jc();
                    align.setVal(JcEnumeration.RIGHT);
                    pprop.setJc(align);
                    p.setPPr(pprop);
                }
                */

                ObjectFactory factory = new ObjectFactory();
                // 新建一个Run
                R run = factory.createR();
                /*
                // 设置段落水平对齐方式
                RPr rpr = run.getRPr();
                if (rpr == null) {
                    rpr = new RPr();
                    run.setRPr(rpr);
                }
                */
                // drawing 画布
                Drawing drawing = factory.createDrawing();
                drawing.getAnchorOrInline().add(anchor);
                run.getContent().add(drawing);
                p.getContent().add(run);
                System.out.println("--------标签已经替换图片--------");
            }
        }
    }


    public static void replaceDrawingWithImage(WordprocessingMLPackage wordMLPackage,
                                               MainDocumentPart documentPart,
                                               String imagePath)
            throws Exception {
        String xPath = "//w:drawing";
        List<Object> list = documentPart.getJAXBNodesViaXPath(xPath, true);
        @SuppressWarnings("unchecked")
        JAXBElement<Drawing> element = (JAXBElement<Drawing>) list.get(0);
        Drawing drawing = element.getValue();
        //获取原图的相关信息，再取创建一个新的图片，用户替换原图
        Anchor anchor = (Anchor) drawing.getAnchorOrInline().get(0);//当前的图片
        Integer posH = anchor.getPositionH().getPosOffset();//原占位图的坐标位置
        Integer posV = anchor.getPositionV().getPosOffset();
        CTNonVisualDrawingProps docPr = anchor.getDocPr();
        int xId = (int) docPr.getId();
        String filenameHint = "picture_to_replace"; //docPr.getName();
        String altText = docPr.getDescr();
        int yId = (int) anchor.getGraphic().getGraphicData().getPic().getNvPicPr().getCNvPr().getId();

        MyBinaryPartAbstractImage imagePart = MyBinaryPartAbstractImage.createImagePart(wordMLPackage, documentPart, new File(imagePath));

        Anchor inline = imagePart.createImageAnchor(wordMLPackage, filenameHint, altText, xId, yId, 400, 300, posH, posV);

        drawing.getAnchorOrInline().set(0, inline);

        System.out.println("--------原图片已经替换--------");


    }

}
